🔙 Home

Snail races are a phenomenon on Twitter in which a Twitter poll is created in which all four of the possible options are labeled simply with the snail emoji (🐌). These polls can get very popular, and are noted to consistently have the same shape of responses. This notebook explores the consistency of Snail Race polls.

Packages used:

library(tidyverse)
library(lme4)
library(reshape2)
library(magick)

Read in the data

As a way of procrastinating, I recorded the results of 150 “snail races” on Twitter. Not all of these polls had closed, and the poll status is recorded in this dataset. In the future, the open polls can be updated.

snail <- read.csv("data/snail_race.csv",header=TRUE,as.is=TRUE)
snail %>%
  group_by(twitteruser)

But what we really want is to melt the four snail types into a single column.

snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  group_by(twitteruser)

Create summary function

I got this function from Cookbook for R. It uses plyr, but I’ve adapted the function to call it directly (plyr::) so that it doesn’t interfere with dplyr elsewhere.

## Summarizes data.
## Gives count, mean, standard deviation, standard error of the mean, and confidence interval (default 95%).
##   data: a data frame.
##   measurevar: the name of a column that contains the variable to be summariezed
##   groupvars: a vector containing names of columns that contain grouping variables
##   na.rm: a boolean that indicates whether to ignore NA's
##   conf.interval: the percent range of the confidence interval (default is 95%)
summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE,
                      conf.interval=.95, .drop=TRUE) {
#    library(plyr)
    # New version of length which can handle NA's: if na.rm==T, don't count them
    length2 <- function (x, na.rm=FALSE) {
        if (na.rm) sum(!is.na(x))
        else       length(x)
    }
    # This does the summary. For each group's data frame, return a vector with
    # N, mean, and sd
    datac <- plyr::ddply(data, groupvars, .drop=.drop,
      .fun = function(xx, col) {
        c(N    = length2(xx[[col]], na.rm=na.rm),
          mean = mean   (xx[[col]], na.rm=na.rm),
          sd   = sd     (xx[[col]], na.rm=na.rm)
        )
      },
      measurevar
    )
    # Rename the "mean" column    
    datac <- plyr::rename(datac, c("mean" = measurevar))
    datac$se <- datac$sd / sqrt(datac$N)  # Calculate standard error of the mean
    # Confidence interval multiplier for standard error
    # Calculate t-statistic for confidence interval: 
    # e.g., if conf.interval is .95, use .975 (above/below), and use df=N-1
    ciMult <- qt(conf.interval/2 + .5, datac$N-1)
    datac$ci <- datac$se * ciMult
    return(datac)
}

Plots

Bar plots

Here is a nice summary of what I’ll be plotting:

snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  summarySE(measurevar="value", groupvars="variable")
  variable   N    value       sd        se        ci
1   snailA 150 16.33333 5.372896 0.4386951 0.8668673
2   snailB 150 28.76000 5.093330 0.4158687 0.8217620
3   snailC 150 42.45333 7.030697 0.5740540 1.1343382
4   snailD 150 12.52000 4.062796 0.3317259 0.6554947

Using the multiplot() function, we can put all three varieties of error bars in one compound figure.

p1 <- snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  summarySE(measurevar="value", groupvars="variable") %>%
  ggplot(aes(x=variable,y=(value/100))) +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(value-sd)/100, ymax=(value+sd)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With standard deviation") +
    ylab("percent of responses") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))
p2 <- snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  summarySE(measurevar="value", groupvars="variable") %>%
  ggplot(aes(x=variable,y=(value/100))) +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(value-ci)/100, ymax=(value+ci)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With 95% Confidence Interval") +
    ylab("") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))
p3 <- snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  summarySE(measurevar="value", groupvars="variable") %>%
  ggplot(aes(x=variable,y=(value/100))) +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(value-se)/100, ymax=(value+se)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With standard error") +
    ylab("") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))
multiplot(p1,p2,p3,cols = 3)

Box plots

Here’s another way to visualize the way these snail races tend to turn out:

snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>%
  ggplot(aes(x=variable,y=value/100)) +
    geom_boxplot(aes(fill=variable)) +
    scale_y_continuous(labels=scales::percent) +
    ggtitle("Distribution of snail race results in quartiles") +
    ylab("percent of responses") + xlab("")

Raincloud plots

Let’s see how a raincloud plot works with these data:

source("https://gist.githubusercontent.com/benmarwick/2a1bb0133ff568cbe28d/raw/fb53bd97121f7f9ce947837ef1a4c65a73bffb3f/geom_flat_violin.R")
raincloud_theme <- theme(
  text = element_text(size = 10),
  axis.title.x = element_text(size = 16),
  axis.title.y = element_text(size = 16),
  axis.text = element_text(size = 14),
  axis.text.x = element_text(angle = 45, vjust = 0.5),
  legend.title = element_text(size = 16),
  legend.text = element_text(size = 16),
  legend.position = "right",
  plot.title = element_text(lineheight = .8, face = "bold", size = 16),
  panel.border = element_blank(),
  panel.grid.minor = element_blank(),
  panel.grid.major = element_blank(),
  axis.line.x = element_line(colour = "black", size = 0.5, linetype = "solid"),
  axis.line.y = element_line(colour = "black", size = 0.5, linetype = "solid"))
snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>% 
    ggplot(aes(x = variable, y = value/100, fill = variable)) +
  ggtitle("Raincloud plot of Twitter Snail Race outcomes") +
    geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .8) +
    geom_point(aes(y = value/100, color = variable), 
              position = position_jitter(width = .15), size = .5, alpha = 0.8) +
    geom_boxplot(width = .1, outlier.shape = NA, alpha = 0.5) +
    scale_x_discrete(limits=c("snailD","snailC","snailB","snailA")) + xlab("") +
    scale_y_continuous(labels=scales::percent) + ylab("percent of responses") +
    expand_limits(x = 5.25) +
    guides(fill = FALSE) +
    guides(color = FALSE) +
    #scale_color_brewer(palette = "Spectral") +
    #scale_fill_brewer(palette = "Spectral") +
    coord_flip() + # flip or not
    theme_bw() +
    raincloud_theme

Hmm… I wonder if I can include a dimension for how many votes each poll got? I’ll try do to that with the size aesthetic, but since there’s such a wide range (21 to over 3 million), I’ll have to take the log of the number of votes.

snail %>%
  melt(measure.vars=c("snailA","snailB","snailC","snailD")) %>% 
    ggplot(aes(x = variable, y = value/100, fill = variable)) +
  ggtitle("Snowcloud plot of Twitter Snail Race outcomes") +
    geom_flat_violin(position = position_nudge(x = .29, y = 0), alpha = .8) +
    geom_point(aes(y = value/100, color = variable, size=log10(votes)), 
              position = position_jitter(width = .2), alpha = 0.35) +
    geom_boxplot(width = .1, outlier.shape = NA, alpha = 0.5) +
    scale_x_discrete(limits=c("snailD","snailC","snailB","snailA")) + xlab("") +
    scale_y_continuous(labels=scales::percent) + ylab("percent of responses") +
    expand_limits(x = 5.25) +
    guides(fill = FALSE) +
    guides(color = FALSE) +
    #scale_color_brewer(palette = "Spectral") +
    #scale_fill_brewer(palette = "Spectral") +
    coord_flip() + # flip or not
    theme_bw() +
    raincloud_theme

Further info

Next steps: I’d like to figure out how to weight each poll by how many responses were received.

Please cite as:

Ackerman, L.M. (2018, May 28). Snail Race. Retrieved from: https://verbingnouns.github.io/notebooks/snail_race.nb.html

BibTeX:

@MISC{snailRace,
author = {Ackerman, Lauren M.},
title = {Snail Race},
month = may,
year = {2018},
howpublished={\url{https://verbingnouns.github.io/notebooks/snail_race.nb.html}}
}

From the first 68 gathered polls:

# Now call back the plot
background <- image_read("images/snail-race-shells.png")
# And bring in a logo
logo_raw <- image_read("images/slug.gif") 
frames <- lapply(logo_raw, function(frame) {
  image_composite(background, frame, offset = "+700+500")
})
animation <- image_animate(image_join(frames))
image_write(animation, "images/slug-graph.gif") # 12 minutes to run?!
beepr::beep()

LS0tCnRpdGxlOiAiU25haWwgUmFjZSIKYXV0aG9yOiAiTGF1cmVuIE0gQWNrZXJtYW4iCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IDI4IE1heSAyMDE4IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgaW5jbHVkZXM6IAogICAgICBpbl9oZWFkZXI6IGdvb2dsZV9hbmFseXRpY3MuaHRtbAotLS0KW/CflJkgSG9tZV0oaHR0cHM6Ly92ZXJiaW5nbm91bnMuZ2l0aHViLmlvL25vdGVib29rcy8pCgpTbmFpbCByYWNlcyBhcmUgYSBwaGVub21lbm9uIG9uIFR3aXR0ZXIgaW4gd2hpY2ggYSBUd2l0dGVyIHBvbGwgaXMgY3JlYXRlZCBpbiB3aGljaCBhbGwgZm91ciBvZiB0aGUgcG9zc2libGUgb3B0aW9ucyBhcmUgbGFiZWxlZCBzaW1wbHkgd2l0aCB0aGUgc25haWwgZW1vamkgKPCfkIwpLiBUaGVzZSBwb2xscyBjYW4gZ2V0IHZlcnkgcG9wdWxhciwgYW5kIGFyZSBub3RlZCB0byBjb25zaXN0ZW50bHkgaGF2ZSB0aGUgc2FtZSBzaGFwZSBvZiByZXNwb25zZXMuIFRoaXMgbm90ZWJvb2sgZXhwbG9yZXMgdGhlIGNvbnNpc3RlbmN5IG9mIFNuYWlsIFJhY2UgcG9sbHMuCgohW10oaW1hZ2VzLzQxOXZpbmNlLnBuZykKClBhY2thZ2VzIHVzZWQ6ICAKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkobWFnaWNrKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgTXVsdGlwbGUgcGxvdCBmdW5jdGlvbgojCiMgZ2dwbG90IG9iamVjdHMgY2FuIGJlIHBhc3NlZCBpbiAuLi4sIG9yIHRvIHBsb3RsaXN0IChhcyBhIGxpc3Qgb2YgZ2dwbG90IG9iamVjdHMpCiMgLSBjb2xzOiAgIE51bWJlciBvZiBjb2x1bW5zIGluIGxheW91dAojIC0gbGF5b3V0OiBBIG1hdHJpeCBzcGVjaWZ5aW5nIHRoZSBsYXlvdXQuIElmIHByZXNlbnQsICdjb2xzJyBpcyBpZ25vcmVkLgojCiMgSWYgdGhlIGxheW91dCBpcyBzb21ldGhpbmcgbGlrZSBtYXRyaXgoYygxLDIsMywzKSwgbnJvdz0yLCBieXJvdz1UUlVFKSwKIyB0aGVuIHBsb3QgMSB3aWxsIGdvIGluIHRoZSB1cHBlciBsZWZ0LCAyIHdpbGwgZ28gaW4gdGhlIHVwcGVyIHJpZ2h0LCBhbmQKIyAzIHdpbGwgZ28gYWxsIHRoZSB3YXkgYWNyb3NzIHRoZSBib3R0b20uCiMKbXVsdGlwbG90IDwtIGZ1bmN0aW9uKC4uLiwgcGxvdGxpc3Q9TlVMTCwgZmlsZSwgY29scz0xLCBsYXlvdXQ9TlVMTCkgewogIGxpYnJhcnkoZ3JpZCkKCiAgIyBNYWtlIGEgbGlzdCBmcm9tIHRoZSAuLi4gYXJndW1lbnRzIGFuZCBwbG90bGlzdAogIHBsb3RzIDwtIGMobGlzdCguLi4pLCBwbG90bGlzdCkKCiAgbnVtUGxvdHMgPSBsZW5ndGgocGxvdHMpCgogICMgSWYgbGF5b3V0IGlzIE5VTEwsIHRoZW4gdXNlICdjb2xzJyB0byBkZXRlcm1pbmUgbGF5b3V0CiAgaWYgKGlzLm51bGwobGF5b3V0KSkgewogICAgIyBNYWtlIHRoZSBwYW5lbAogICAgIyBuY29sOiBOdW1iZXIgb2YgY29sdW1ucyBvZiBwbG90cwogICAgIyBucm93OiBOdW1iZXIgb2Ygcm93cyBuZWVkZWQsIGNhbGN1bGF0ZWQgZnJvbSAjIG9mIGNvbHMKICAgIGxheW91dCA8LSBtYXRyaXgoc2VxKDEsIGNvbHMgKiBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKSwKICAgICAgICAgICAgICAgICAgICBuY29sID0gY29scywgbnJvdyA9IGNlaWxpbmcobnVtUGxvdHMvY29scykpCiAgfQoKIGlmIChudW1QbG90cz09MSkgewogICAgcHJpbnQocGxvdHNbWzFdXSkKCiAgfSBlbHNlIHsKICAgICMgU2V0IHVwIHRoZSBwYWdlCiAgICBncmlkLm5ld3BhZ2UoKQogICAgcHVzaFZpZXdwb3J0KHZpZXdwb3J0KGxheW91dCA9IGdyaWQubGF5b3V0KG5yb3cobGF5b3V0KSwgbmNvbChsYXlvdXQpKSkpCgogICAgIyBNYWtlIGVhY2ggcGxvdCwgaW4gdGhlIGNvcnJlY3QgbG9jYXRpb24KICAgIGZvciAoaSBpbiAxOm51bVBsb3RzKSB7CiAgICAgICMgR2V0IHRoZSBpLGogbWF0cml4IHBvc2l0aW9ucyBvZiB0aGUgcmVnaW9ucyB0aGF0IGNvbnRhaW4gdGhpcyBzdWJwbG90CiAgICAgIG1hdGNoaWR4IDwtIGFzLmRhdGEuZnJhbWUod2hpY2gobGF5b3V0ID09IGksIGFyci5pbmQgPSBUUlVFKSkKCiAgICAgIHByaW50KHBsb3RzW1tpXV0sIHZwID0gdmlld3BvcnQobGF5b3V0LnBvcy5yb3cgPSBtYXRjaGlkeCRyb3csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0LnBvcy5jb2wgPSBtYXRjaGlkeCRjb2wpKQogICAgfQogIH0KfQpgYGAKCiMgUmVhZCBpbiB0aGUgZGF0YQoKQXMgYSB3YXkgb2YgcHJvY3Jhc3RpbmF0aW5nLCBJIHJlY29yZGVkIHRoZSByZXN1bHRzIG9mIDE1MCAic25haWwgcmFjZXMiIG9uIFtUd2l0dGVyXShodHRwczovL3R3aXR0ZXIuY29tL3NlYXJjaD9xPXNuYWlsJTIwcmFjZSZzcmM9dHlwZCkuIE5vdCBhbGwgb2YgdGhlc2UgcG9sbHMgaGFkIGNsb3NlZCwgYW5kIHRoZSBwb2xsIHN0YXR1cyBpcyByZWNvcmRlZCBpbiB0aGlzIGRhdGFzZXQuIEluIHRoZSBmdXR1cmUsIHRoZSBvcGVuIHBvbGxzIGNhbiBiZSB1cGRhdGVkLgoKIVtdKGltYWdlcy9zbmFpbC1yYWNlLXNlYXJjaC5wbmcpCgpgYGB7cn0Kc25haWwgPC0gcmVhZC5jc3YoImRhdGEvc25haWxfcmFjZS5jc3YiLGhlYWRlcj1UUlVFLGFzLmlzPVRSVUUpCgpzbmFpbCAlPiUKICBncm91cF9ieSh0d2l0dGVydXNlcikKYGBgCgpCdXQgd2hhdCB3ZSByZWFsbHkgd2FudCBpcyB0byBtZWx0IHRoZSBmb3VyIHNuYWlsIHR5cGVzIGludG8gYSBzaW5nbGUgY29sdW1uLgoKYGBge3J9CnNuYWlsICU+JQogIG1lbHQobWVhc3VyZS52YXJzPWMoInNuYWlsQSIsInNuYWlsQiIsInNuYWlsQyIsInNuYWlsRCIpKSAlPiUKICBncm91cF9ieSh0d2l0dGVydXNlcikKYGBgCgoKIyBDcmVhdGUgc3VtbWFyeSBmdW5jdGlvbgoKSSBnb3QgdGhpcyBmdW5jdGlvbiBmcm9tIFtDb29rYm9vayBmb3IgUl0oaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvUGxvdHRpbmdfbWVhbnNfYW5kX2Vycm9yX2JhcnNfKGdncGxvdDIpKS4gSXQgdXNlcyBgcGx5cmAsIGJ1dCBJJ3ZlIGFkYXB0ZWQgdGhlIGZ1bmN0aW9uIHRvIGNhbGwgaXQgZGlyZWN0bHkgKGBwbHlyOjpgKSBzbyB0aGF0IGl0IGRvZXNuJ3QgaW50ZXJmZXJlIHdpdGggYGRwbHlyYCBlbHNld2hlcmUuCgpgYGB7cn0KIyMgU3VtbWFyaXplcyBkYXRhLgojIyBHaXZlcyBjb3VudCwgbWVhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiwgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWwgKGRlZmF1bHQgOTUlKS4KIyMgICBkYXRhOiBhIGRhdGEgZnJhbWUuCiMjICAgbWVhc3VyZXZhcjogdGhlIG5hbWUgb2YgYSBjb2x1bW4gdGhhdCBjb250YWlucyB0aGUgdmFyaWFibGUgdG8gYmUgc3VtbWFyaWV6ZWQKIyMgICBncm91cHZhcnM6IGEgdmVjdG9yIGNvbnRhaW5pbmcgbmFtZXMgb2YgY29sdW1ucyB0aGF0IGNvbnRhaW4gZ3JvdXBpbmcgdmFyaWFibGVzCiMjICAgbmEucm06IGEgYm9vbGVhbiB0aGF0IGluZGljYXRlcyB3aGV0aGVyIHRvIGlnbm9yZSBOQSdzCiMjICAgY29uZi5pbnRlcnZhbDogdGhlIHBlcmNlbnQgcmFuZ2Ugb2YgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgKGRlZmF1bHQgaXMgOTUlKQpzdW1tYXJ5U0UgPC0gZnVuY3Rpb24oZGF0YT1OVUxMLCBtZWFzdXJldmFyLCBncm91cHZhcnM9TlVMTCwgbmEucm09RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBjb25mLmludGVydmFsPS45NSwgLmRyb3A9VFJVRSkgewojICAgIGxpYnJhcnkocGx5cikKCiAgICAjIE5ldyB2ZXJzaW9uIG9mIGxlbmd0aCB3aGljaCBjYW4gaGFuZGxlIE5BJ3M6IGlmIG5hLnJtPT1ULCBkb24ndCBjb3VudCB0aGVtCiAgICBsZW5ndGgyIDwtIGZ1bmN0aW9uICh4LCBuYS5ybT1GQUxTRSkgewogICAgICAgIGlmIChuYS5ybSkgc3VtKCFpcy5uYSh4KSkKICAgICAgICBlbHNlICAgICAgIGxlbmd0aCh4KQogICAgfQoKICAgICMgVGhpcyBkb2VzIHRoZSBzdW1tYXJ5LiBGb3IgZWFjaCBncm91cCdzIGRhdGEgZnJhbWUsIHJldHVybiBhIHZlY3RvciB3aXRoCiAgICAjIE4sIG1lYW4sIGFuZCBzZAogICAgZGF0YWMgPC0gcGx5cjo6ZGRwbHkoZGF0YSwgZ3JvdXB2YXJzLCAuZHJvcD0uZHJvcCwKICAgICAgLmZ1biA9IGZ1bmN0aW9uKHh4LCBjb2wpIHsKICAgICAgICBjKE4gICAgPSBsZW5ndGgyKHh4W1tjb2xdXSwgbmEucm09bmEucm0pLAogICAgICAgICAgbWVhbiA9IG1lYW4gICAoeHhbW2NvbF1dLCBuYS5ybT1uYS5ybSksCiAgICAgICAgICBzZCAgID0gc2QgICAgICh4eFtbY29sXV0sIG5hLnJtPW5hLnJtKQogICAgICAgICkKICAgICAgfSwKICAgICAgbWVhc3VyZXZhcgogICAgKQoKICAgICMgUmVuYW1lIHRoZSAibWVhbiIgY29sdW1uICAgIAogICAgZGF0YWMgPC0gcGx5cjo6cmVuYW1lKGRhdGFjLCBjKCJtZWFuIiA9IG1lYXN1cmV2YXIpKQoKICAgIGRhdGFjJHNlIDwtIGRhdGFjJHNkIC8gc3FydChkYXRhYyROKSAgIyBDYWxjdWxhdGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4KCiAgICAjIENvbmZpZGVuY2UgaW50ZXJ2YWwgbXVsdGlwbGllciBmb3Igc3RhbmRhcmQgZXJyb3IKICAgICMgQ2FsY3VsYXRlIHQtc3RhdGlzdGljIGZvciBjb25maWRlbmNlIGludGVydmFsOiAKICAgICMgZS5nLiwgaWYgY29uZi5pbnRlcnZhbCBpcyAuOTUsIHVzZSAuOTc1IChhYm92ZS9iZWxvdyksIGFuZCB1c2UgZGY9Ti0xCiAgICBjaU11bHQgPC0gcXQoY29uZi5pbnRlcnZhbC8yICsgLjUsIGRhdGFjJE4tMSkKICAgIGRhdGFjJGNpIDwtIGRhdGFjJHNlICogY2lNdWx0CgogICAgcmV0dXJuKGRhdGFjKQp9CmBgYAoKIyBQbG90cwoKIyMgQmFyIHBsb3RzCgpIZXJlIGlzIGEgbmljZSBzdW1tYXJ5IG9mIHdoYXQgSSdsbCBiZSBwbG90dGluZzoKCmBgYHtyfQpzbmFpbCAlPiUKICBtZWx0KG1lYXN1cmUudmFycz1jKCJzbmFpbEEiLCJzbmFpbEIiLCJzbmFpbEMiLCJzbmFpbEQiKSkgJT4lCiAgc3VtbWFyeVNFKG1lYXN1cmV2YXI9InZhbHVlIiwgZ3JvdXB2YXJzPSJ2YXJpYWJsZSIpCmBgYAoKVXNpbmcgdGhlIFtgbXVsdGlwbG90KClgIGZ1bmN0aW9uXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9NdWx0aXBsZV9ncmFwaHNfb25fb25lX3BhZ2VfKGdncGxvdDIpLyksIHdlIGNhbiBwdXQgYWxsIHRocmVlIHZhcmlldGllcyBvZiBlcnJvciBiYXJzIGluIG9uZSBjb21wb3VuZCBmaWd1cmUuCgpgYGBge3IgZmlnLndpZHRoPTQuNzUsIGZpZy5hc3A9MC41fQpwMSA8LSBzbmFpbCAlPiUKICBtZWx0KG1lYXN1cmUudmFycz1jKCJzbmFpbEEiLCJzbmFpbEIiLCJzbmFpbEMiLCJzbmFpbEQiKSkgJT4lCiAgc3VtbWFyeVNFKG1lYXN1cmV2YXI9InZhbHVlIiwgZ3JvdXB2YXJzPSJ2YXJpYWJsZSIpICU+JQogIGdncGxvdChhZXMoeD12YXJpYWJsZSx5PSh2YWx1ZS8xMDApKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9dmFyaWFibGUpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj0odmFsdWUtc2QpLzEwMCwgeW1heD0odmFsdWUrc2QpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCBzdGFuZGFyZCBkZXZpYXRpb24iKSArCiAgICB5bGFiKCJwZXJjZW50IG9mIHJlc3BvbnNlcyIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgpwMiA8LSBzbmFpbCAlPiUKICBtZWx0KG1lYXN1cmUudmFycz1jKCJzbmFpbEEiLCJzbmFpbEIiLCJzbmFpbEMiLCJzbmFpbEQiKSkgJT4lCiAgc3VtbWFyeVNFKG1lYXN1cmV2YXI9InZhbHVlIiwgZ3JvdXB2YXJzPSJ2YXJpYWJsZSIpICU+JQogIGdncGxvdChhZXMoeD12YXJpYWJsZSx5PSh2YWx1ZS8xMDApKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9dmFyaWFibGUpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj0odmFsdWUtY2kpLzEwMCwgeW1heD0odmFsdWUrY2kpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCA5NSUgQ29uZmlkZW5jZSBJbnRlcnZhbCIpICsKICAgIHlsYWIoIiIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgpwMyA8LSBzbmFpbCAlPiUKICBtZWx0KG1lYXN1cmUudmFycz1jKCJzbmFpbEEiLCJzbmFpbEIiLCJzbmFpbEMiLCJzbmFpbEQiKSkgJT4lCiAgc3VtbWFyeVNFKG1lYXN1cmV2YXI9InZhbHVlIiwgZ3JvdXB2YXJzPSJ2YXJpYWJsZSIpICU+JQogIGdncGxvdChhZXMoeD12YXJpYWJsZSx5PSh2YWx1ZS8xMDApKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9dmFyaWFibGUpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj0odmFsdWUtc2UpLzEwMCwgeW1heD0odmFsdWUrc2UpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCBzdGFuZGFyZCBlcnJvciIpICsKICAgIHlsYWIoIiIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgptdWx0aXBsb3QocDEscDIscDMsY29scyA9IDMpCmBgYGAKCiMjIEJveCBwbG90cwoKSGVyZSdzIGFub3RoZXIgd2F5IHRvIHZpc3VhbGl6ZSB0aGUgd2F5IHRoZXNlIHNuYWlsIHJhY2VzIHRlbmQgdG8gdHVybiBvdXQ6CgpgYGBge3J9CnNuYWlsICU+JQogIG1lbHQobWVhc3VyZS52YXJzPWMoInNuYWlsQSIsInNuYWlsQiIsInNuYWlsQyIsInNuYWlsRCIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9dmFyaWFibGUseT12YWx1ZS8xMDApKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dmFyaWFibGUpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogICAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHNuYWlsIHJhY2UgcmVzdWx0cyBpbiBxdWFydGlsZXMiKSArCiAgICB5bGFiKCJwZXJjZW50IG9mIHJlc3BvbnNlcyIpICsgeGxhYigiIikKYGBgYAoKIyMgUmFpbmNsb3VkIHBsb3RzCgpMZXQncyBzZWUgaG93IGEgW3JhaW5jbG91ZCBwbG90XShodHRwczovL29yY2hpZDAwLmdpdGh1Yi5pby90aWR5X3JhaW5jbG91ZHBsb3QpIHdvcmtzIHdpdGggdGhlc2UgZGF0YToKCmBgYHtyfQpzb3VyY2UoImh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vYmVubWFyd2ljay8yYTFiYjAxMzNmZjU2OGNiZTI4ZC9yYXcvZmI1M2JkOTcxMjFmN2Y5Y2U5NDc4MzdlZjFhNGM2NWE3M2JmZmIzZi9nZW9tX2ZsYXRfdmlvbGluLlIiKQoKcmFpbmNsb3VkX3RoZW1lIDwtIHRoZW1lKAogIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSksCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQobGluZWhlaWdodCA9IC44LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTYpLAogIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAic29saWQiKSwKICBheGlzLmxpbmUueSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41LCBsaW5ldHlwZSA9ICJzb2xpZCIpKQpgYGAKCgpgYGBge3J9CnNuYWlsICU+JQogIG1lbHQobWVhc3VyZS52YXJzPWMoInNuYWlsQSIsInNuYWlsQiIsInNuYWlsQyIsInNuYWlsRCIpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSB2YXJpYWJsZSwgeSA9IHZhbHVlLzEwMCwgZmlsbCA9IHZhcmlhYmxlKSkgKwogIGdndGl0bGUoIlJhaW5jbG91ZCBwbG90IG9mIFR3aXR0ZXIgU25haWwgUmFjZSBvdXRjb21lcyIpICsKICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gLjIsIHkgPSAwKSwgYWxwaGEgPSAuOCkgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IHZhbHVlLzEwMCwgY29sb3IgPSB2YXJpYWJsZSksIAogICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gLjE1KSwgc2l6ZSA9IC41LCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gLjEsIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjUpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPWMoInNuYWlsRCIsInNuYWlsQyIsInNuYWlsQiIsInNuYWlsQSIpKSArIHhsYWIoIiIpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArIHlsYWIoInBlcmNlbnQgb2YgcmVzcG9uc2VzIikgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gNS4yNSkgKwogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogICAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICAgICNzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICAgICNzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogICAgY29vcmRfZmxpcCgpICsgIyBmbGlwIG9yIG5vdAogICAgdGhlbWVfYncoKSArCiAgICByYWluY2xvdWRfdGhlbWUKYGBgYAoKSG1t4oCmIEkgd29uZGVyIGlmIEkgY2FuIGluY2x1ZGUgYSBkaW1lbnNpb24gZm9yIGhvdyBtYW55IHZvdGVzIGVhY2ggcG9sbCBnb3Q/IEknbGwgdHJ5IGRvIHRvIHRoYXQgd2l0aCB0aGUgYHNpemVgIGFlc3RoZXRpYywgYnV0IHNpbmNlIHRoZXJlJ3Mgc3VjaCBhIHdpZGUgcmFuZ2UgKDIxIHRvIG92ZXIgMyBtaWxsaW9uKSwgSSdsbCBoYXZlIHRvIHRha2UgdGhlIGxvZyBvZiB0aGUgbnVtYmVyIG9mIHZvdGVzLgoKYGBge3J9CnNuYWlsICU+JQogIG1lbHQobWVhc3VyZS52YXJzPWMoInNuYWlsQSIsInNuYWlsQiIsInNuYWlsQyIsInNuYWlsRCIpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSB2YXJpYWJsZSwgeSA9IHZhbHVlLzEwMCwgZmlsbCA9IHZhcmlhYmxlKSkgKwogIGdndGl0bGUoIlNub3djbG91ZCBwbG90IG9mIFR3aXR0ZXIgU25haWwgUmFjZSBvdXRjb21lcyIpICsKICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gLjI5LCB5ID0gMCksIGFscGhhID0gLjgpICsKICAgIGdlb21fcG9pbnQoYWVzKHkgPSB2YWx1ZS8xMDAsIGNvbG9yID0gdmFyaWFibGUsIHNpemU9bG9nMTAodm90ZXMpKSwgCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAuMiksIGFscGhhID0gMC4zNSkgKwogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gLjEsIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjUpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPWMoInNuYWlsRCIsInNuYWlsQyIsInNuYWlsQiIsInNuYWlsQSIpKSArIHhsYWIoIiIpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArIHlsYWIoInBlcmNlbnQgb2YgcmVzcG9uc2VzIikgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gNS4yNSkgKwogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogICAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICAgICNzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICAgICNzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogICAgY29vcmRfZmxpcCgpICsgIyBmbGlwIG9yIG5vdAogICAgdGhlbWVfYncoKSArCiAgICByYWluY2xvdWRfdGhlbWUKYGBgCgojIEZ1cnRoZXIgaW5mbwoKKipOZXh0IHN0ZXBzOioqIEknZCBsaWtlIHRvIGZpZ3VyZSBvdXQgaG93IHRvIHdlaWdodCBlYWNoIHBvbGwgYnkgaG93IG1hbnkgcmVzcG9uc2VzIHdlcmUgcmVjZWl2ZWQuCgpQbGVhc2UgY2l0ZSBhczoKCj4gQWNrZXJtYW4sIEwuTS4gKDIwMTgsIE1heSAyOCkuICpTbmFpbCBSYWNlKi4gUmV0cmlldmVkIGZyb206IFtodHRwczovL3ZlcmJpbmdub3Vucy5naXRodWIuaW8vbm90ZWJvb2tzL3NuYWlsX3JhY2UubmIuaHRtbF0oKQoKQmliVGVYOgoKYGBgCkBNSVNDe3NuYWlsUmFjZSwKYXV0aG9yID0ge0Fja2VybWFuLCBMYXVyZW4gTS59LAp0aXRsZSA9IHtTbmFpbCBSYWNlfSwKbW9udGggPSBtYXksCnllYXIgPSB7MjAxOH0sCmhvd3B1Ymxpc2hlZD17XHVybHtodHRwczovL3ZlcmJpbmdub3Vucy5naXRodWIuaW8vbm90ZWJvb2tzL3NuYWlsX3JhY2UubmIuaHRtbH19Cn0KYGBgCgoqKioKCkZyb20gdGhlIFtmaXJzdCA2OCBnYXRoZXJlZCBwb2xsc10oaHR0cHM6Ly90d2l0dGVyLmNvbS9WZXJiaW5nTm91bnMvc3RhdHVzLzEwMDA3NzUwOTUyOTQ1ODY4ODApOgoKYGBgYHtyfQojIE5vdyBjYWxsIGJhY2sgdGhlIHBsb3QKYmFja2dyb3VuZCA8LSBpbWFnZV9yZWFkKCJpbWFnZXMvc25haWwtcmFjZS1zaGVsbHMucG5nIikKIyBBbmQgYnJpbmcgaW4gYSBsb2dvCmxvZ29fcmF3IDwtIGltYWdlX3JlYWQoImltYWdlcy9zbHVnLmdpZiIpIApmcmFtZXMgPC0gbGFwcGx5KGxvZ29fcmF3LCBmdW5jdGlvbihmcmFtZSkgewogIGltYWdlX2NvbXBvc2l0ZShiYWNrZ3JvdW5kLCBmcmFtZSwgb2Zmc2V0ID0gIis3MDArNTAwIikKfSkKYW5pbWF0aW9uIDwtIGltYWdlX2FuaW1hdGUoaW1hZ2Vfam9pbihmcmFtZXMpKQppbWFnZV93cml0ZShhbmltYXRpb24sICJpbWFnZXMvc2x1Zy1ncmFwaC5naWYiKSAjIDEyIG1pbnV0ZXMgdG8gcnVuPyEKYmVlcHI6OmJlZXAoKQpgYGBgCgohW10oaW1hZ2VzL3NsdWctZ3JhcGguZ2lmKQ==